import { GlFormTextarea } from '@gitlab/ui';
import { nextTick } from 'vue';
import Tracking from '~/tracking';
import { FEEDBACK_OPTIONS } from 'ee/ai/constants';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ExplainVulnerabilityUserFeedback, {
  getFileExtension,
} from 'ee/vulnerabilities/components/explain_vulnerability/explain_vulnerability_user_feedback.vue';

const MOCK_VULNERABILITY = {
  id: 1,
  location: {
    file: 'package.json',
  },
  severity: 'high',
  identifiers: [
    {
      name: 'CWE-100',
    },
    {
      name: 'CWE-200',
    },
  ],
};

const expectedTracking = (vulnerability = MOCK_VULNERABILITY) => {
  const { identifiers, id, severity } = vulnerability;
  const eventName = 'explain_vulnerability';

  expect(Tracking.event).toHaveBeenCalledWith(
    undefined,
    eventName,
    expect.objectContaining({
      action: 'click_button',
      label: 'response_feedback',
      property: FEEDBACK_OPTIONS[0].value,
      extra: expect.objectContaining({
        vulnerability_id: id,
        identifiers: expect.arrayContaining(identifiers.map(({ name }) => name)),
        file_extension: 'json',
        severity,
      }),
    }),
  );
};

describe('ExplainVulnerabilityUserFeedback', () => {
  let wrapper;

  const createComponent = () => {
    wrapper = shallowMountExtended(ExplainVulnerabilityUserFeedback, {
      propsData: {
        vulnerability: MOCK_VULNERABILITY,
      },
    });
  };

  const findFeedbackButtons = () => wrapper.findAllByTestId('feedback-button');
  const firstFeedbackButton = () => findFeedbackButtons().at(0);
  const findSubmitButton = () => wrapper.find('[type="submit"]');
  const findTextArea = () => wrapper.findComponent(GlFormTextarea);

  beforeEach(() => {
    jest.spyOn(Tracking, 'event');
  });

  describe('basic structure', () => {
    beforeEach(() => {
      createComponent();
    });

    it('renders buttons based on provided options', () => {
      expect(findFeedbackButtons()).toHaveLength(FEEDBACK_OPTIONS.length);
    });

    it('should render a textarea for the user comment', () => {
      expect(findTextArea().exists()).toBe(true);
    });
  });

  describe('button', () => {
    beforeEach(() => {
      createComponent();
    });

    it('has correct text', () => {
      expect(firstFeedbackButton().text()).toBe(FEEDBACK_OPTIONS[0].title);
    });

    it('receives correct icon prop', () => {
      expect(firstFeedbackButton().props('icon')).toBe(FEEDBACK_OPTIONS[0].icon);
    });
  });

  describe('tracking', () => {
    beforeEach(() => {
      createComponent();
    });

    it('fires tracking event with the correct data and information extracted from the vulnerability prop', () => {
      firstFeedbackButton().vm.$emit('click');
      findSubmitButton().vm.$emit('click');

      expectedTracking();
    });

    it('fires tracking event when component is destroyed if button was clicked', () => {
      firstFeedbackButton().vm.$emit('click');
      findSubmitButton().vm.$emit('click');

      expectedTracking();
    });

    it('fires tracking event when the window is closed', () => {
      firstFeedbackButton().vm.$emit('click');
      findSubmitButton().vm.$emit('click');

      expectedTracking();
    });

    it('shows only selected option with disabled state once feedback is provided', async () => {
      const selectedButtonIndex = 2;

      expect(findFeedbackButtons()).toHaveLength(3);

      findFeedbackButtons().at(selectedButtonIndex).vm.$emit('click');
      findSubmitButton().vm.$emit('click');
      await nextTick();

      const savedFeedbackButtons = findFeedbackButtons();
      const savedFeedbackButton = savedFeedbackButtons.at(0);
      expect(savedFeedbackButtons).toHaveLength(1);
      expect(savedFeedbackButtons.exists()).toBe(true);
      expect(savedFeedbackButton.attributes('disabled')).toBeDefined();
      expect(savedFeedbackButton.text()).toBe(FEEDBACK_OPTIONS[selectedButtonIndex].title);
    });
  });

  describe('submit', () => {
    beforeEach(() => {
      createComponent();
    });

    it('should have the submit button disabled initially', () => {
      expect(findSubmitButton().attributes('disabled')).toBeDefined();
    });

    it('enables the submit button once a feedback option is clicked', async () => {
      await firstFeedbackButton().vm.$emit('click');
      expect(findSubmitButton().attributes('disabled')).toBeUndefined();
    });

    it('does not display textarea and submit button after the form is submitted', async () => {
      firstFeedbackButton().vm.$emit('click');
      findSubmitButton().vm.$emit('click');

      await nextTick();

      expect(findTextArea().exists()).toBe(false);
      expect(findSubmitButton().exists()).toBe(false);
    });
  });

  describe('getFileExtension', () => {
    it.each`
      file                                 | expected
      ${''}                                | ${''}
      ${null}                              | ${''}
      ${undefined}                         | ${''}
      ${'src/html/'}                       | ${''}
      ${'server.com/abc.123/thing'}        | ${''}
      ${'src/html/index.html'}             | ${'html'}
      ${'thing.html.erb'}                  | ${'html.erb'}
      ${'/abc/thing.text.erb'}             | ${'text.erb'}
      ${'/abc/index.thing.text.erb'}       | ${'thing.text.erb'}
      ${'/abc/.thing'}                     | ${'thing'}
      ${'abc/index.html'}                  | ${'html'}
      ${'server.com/abc.123/thing.rb.erb'} | ${'rb.erb'}
    `('renders $expected for $file', ({ file, expected }) => {
      expect(getFileExtension(file)).toBe(expected);
    });
  });
});
